#include "Image.h"
#include "d_Math.h"
#include "camera.h"
#include <fstream>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
#include <cstdlib>
#include <vector>
using namespace d_Math;

#ifndef MRPADSMESH_H
#define MRMESH_H

struct MRCamera
{
	d_Vec3f vp;                     //Position
	d_Vec3f d;			//Direction
	d_Vec3f xa;			//Horizontal axis
	d_Vec3f ya;			//Up vector
	float sx, sy, cx, cy, focal;	//Attributes, scaling, center and focal point
};

class Splat
{
public:
	Splat(){}
	int id;			//ID
	d_Vec3f vp;		//Position
	d_Vec3f n;		//Normal
	d_Vec3f c;		//Color
	Splat operator + (Splat s) const /* Helper function to average splats */
	{
		Splat ret;
		ret.vp = vp+s.vp;
		ret.n = n+s.n;
		ret.c = c +s.c;
		return ret;
	}
	Splat operator / (float a) const /* Helper function to average splats */
	{
		Splat ret;
		ret.vp = vp/a;
		ret.n = n;
		ret.c = c/a;
		return ret;
	}
};

class MRSection
{
public:
	MRSection(int level, int depth);
	void compute_splats(int bb_x, int bb_y, int bb_w, int bb_h, int shiftx, int shifty, 
				Image *p, Image *n, Image *c, MRCamera cam, 
				float dist, float dx, std::vector<Splat*> *splats_vec);
	Splat get_average_splat();      //Get the average of position, normal and color from child splats
	void draw(int d, d_Frustum *frustum, bool visible=false); //Draw function that takes a frustum for aabb and backface culling
	void draw(int d);         			//Draw function that draws everything at tree level 'd'
	void draw_normals();      			//Draw normals as lines pointing outward for reference
	bool is_front_facing(d_Frustum *frustum); 	//Backface culling test
	void compute_aabb();				//Compute bounding box of the section 
	void compute_ncone();				//Compute normal cone of the section

	//Aux functions///
	void drawColor(int d);		//Draws color of points without lighting (albedo)
	void drawNormals(int d);	//Draws normals as color of points
	void drawSmoothNormals(int d);	//Draws smooth normals as color of points
	void drawID(int d);		//Encodes ID's of splats into RGB color and draws it
	//////////////////

	d_Vecf get_local_sizes(int size);

//private:
	d_AABB *aabb;		//Bounding box for frustum culling
	d_NormalCone *ncone;	//Normal cone for culling sections not pointing to camera
	int level;		//the depth of the tree where the section is
	int num_childs;		//Childs of section, given in powers of 4 (quadtree)
	Splat *splats;		//Splats that define this level of resolution for chart
	MRSection **childs;	//Pointer to the child sections, may be NULL
	int num_splats;		//Number of splats (at most 4)
};

class MRChart
{
public:
	MRChart(int index, int depth, int min_chart, int sec_w, int sec_h, float dx, float dist, int bb_x, int bb_y, MRCamera cam, Image *p, Image *n, Image *c, std::vector<Splat*> *splats);
	void draw(int d, d_Frustum *frustum);
	void draw(int d);
	void generate_tree(std::vector<Splat*> *splats);
	Splat get_average_splat();
	void compute_aabb();

	//Aux functions///
	void drawColor(int d);
	void drawNormals(int d);
	void drawSmoothNormals(int d);
	void drawID(int d);
	//////////////////

	d_Vecf get_local_sizes(int size);
private:
	int index;
	int sections_w;
	int sections_h;
	int num_tops;
	int depth;
	d_AABB *aabb;
	MRSection **top;
	Image *p;
	Image *n;
	Image *c;

	float dx;
	float dist;
	int bb_x;
	int bb_y;
	MRCamera cam;
};

class MRMesh
{
public:
	MRMesh(char *filename);
	void draw(float target_fps, Camera cam);
	void draw(Camera cam);
	void draw();

	//Aux functions///
	void drawColor();
	void drawNormals();
	void drawSmoothNormals();
	void drawID();

	void setSplatColor(int id, float r, float g, float b);
	void setSplatNormal(int id, float xd, float yd, float zd);
	//////////////////

	void depth_change(int dir);
	void compute_aabb();
	d_Vec3f get_center();

	int get_minchart();
	int get_treedepth(int index, float target_size);
	float get_realbbw(int cindex);
	float get_realbbh(int cindex);
	int get_targetsize(int index);
	int get_top_of_chartw(int index, int size);
	int get_top_of_charth(int index, int size);

	float get_pointdist(int minchart, int level);
	
	int get_total_splats(){return splats.size();}
private:
	MRChart **charts;
	int num_charts;
	int tree_depth;
	int current_depth;
	int last_draw;
	int num_sections;
	d_AABB *aabb;
	d_Frustum frustum;

	std::vector<Splat*> splats;

	MRCamera *cameras;
	int num_cams;
	int *bb_w, *bb_h;
	int *bb_x, *bb_y;
	float *dist, *dx;
	int *look_up;
	Image **p;
	Image **n;
	Image **c;

	//Average point radius in the different levels of the MRTree
	d_Vecf point_sizes;
};

#endif

